#include "util_time.h"
#include <cerrno>
#include <chrono>
#include <ctime>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdint.h>
#include <string>
#include <sys/time.h>

namespace util::time
{
uint64_t GetTimeStamp()
{
    auto now = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
}

uint64_t GetTimeStampMs()
{
    auto now = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
}

std::string GetCurrentTimeStr(bool ms, const char* format)
{
    using namespace std::chrono;
    auto    now        = system_clock::now();
    auto    now_time_t = system_clock::to_time_t(now);
    std::tm tm         = *std::localtime(&now_time_t);

    std::ostringstream oss;
    oss << std::put_time(&tm, format);
    if (ms)
    {
        auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
        oss << '.' << std::setw(3) << std::setfill('0') << ms.count();
    }
    return std::move(oss.str());
}

long long GetMillisecondsUntil(const std::string& until_time)
{
    auto               now = std::chrono::system_clock::now();
    std::istringstream ss(until_time);
    std::tm            target_tm = {};
    ss >> std::get_time(&target_tm, "%Y-%m-%d %H:%M:%S");

    if (ss.fail())
    {
        throw std::runtime_error("Failed to parse target time string");
    }

    std::time_t target_time_t = std::mktime(&target_tm);

    if (target_time_t == -1)
    {
        throw std::runtime_error("failed to convert target time to time_t");
    }

    auto target_point = std::chrono::system_clock::from_time_t(target_time_t);

    auto duration     = target_point - now;
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

    return milliseconds;
}

uint64_t GetMillisecondsToMidnight()
{
    // 获取当前时间
    auto    now        = std::chrono::system_clock::now();
    auto    now_time_t = std::chrono::system_clock::to_time_t(now);
    std::tm tm_now     = *std::localtime(&now_time_t);

    tm_now.tm_hour  = 23;
    tm_now.tm_min   = 59;
    tm_now.tm_sec   = 59;
    tm_now.tm_isdst = -1;  // 处理夏令时

    // 将午夜时间点转化为时间点
    auto midnight_time_t = std::mktime(&tm_now);
    auto midnight        = std::chrono::system_clock::from_time_t(midnight_time_t) + std::chrono::seconds(1);

    auto duration     = midnight - now;
    auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    return milliseconds;
}

std::string ConvertTimestamp(const std::string& input, const std::string& fm)
{
    std::istringstream ss(input);
    std::tm            tm = {};
    ss >> std::get_time(&tm, fm.c_str());

    // 检查是否解析成功
    if (ss.fail())
    {
        throw std::invalid_argument("Invalid time format");
    }

    // 转换为 time_point
    std::chrono::system_clock::time_point tp = std::chrono::system_clock::from_time_t(std::mktime(&tm)) + std::chrono::hours(8);

    // 获取毫秒
    auto millisec = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()) % 1000;

    // 将 time_point 转换为 std::tm
    std::time_t time      = std::chrono::system_clock::to_time_t(tp);
    std::tm*    time_info = std::localtime(&time);

    // 输出格式化的时间
    std::ostringstream formatted;
    formatted << std::put_time(time_info, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0') << millisec.count();
    return formatted.str();
}

std::string TimePointToString(const std::chrono::system_clock::time_point& tp)
{
    std::time_t       t  = std::chrono::system_clock::to_time_t(tp);
    std::tm           tm = *std::localtime(&t);  // 转换为本地时间
    std::stringstream ss;
    ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

std::chrono::system_clock::time_point FormatTimeToTimePoint(const std::string& timeStr)
{
    auto               now = std::chrono::system_clock::now();
    std::istringstream ss(timeStr);
    std::tm            target_tm = {};
    ss >> std::get_time(&target_tm, "%Y-%m-%d %H:%M:%S");

    if (ss.fail())
    {
        throw std::runtime_error("Failed to parse target time string");
    }

    std::time_t target_time_t = std::mktime(&target_tm);

    if (target_time_t == -1)
    {
        throw std::runtime_error("failed to convert target time to time_t");
    }

    return std::chrono::system_clock::from_time_t(target_time_t);
}

long long CalculateNextExecutionTime(const std::string& firstExecTime, int periodValue /*多少个单位*/,
                                     const std::string& periodUnit /*周期单位*/)
{
    if (periodValue <= 0)
    {
        return 0;
    }

    // 获取当前时间和首次执行时间
    auto now                = std::chrono::system_clock::now();
    auto firstExecTimePoint = FormatTimeToTimePoint(firstExecTime);

    // std::cout << "Current time: " << TimePointToString(now) << std::endl;
    // std::cout << "First execution time: " << TimePointToString(firstExecTimePoint) << std::endl;

    // 如果当前时间在首次执行时间之前，直接返回距离首次执行时间的秒数
    if (now < firstExecTimePoint)
    {
        auto secondsUntilFirstExecution = std::chrono::duration_cast<std::chrono::milliseconds>(firstExecTimePoint - now).count();
        // std::cout << "First execution time is in the future. Seconds until first execution: " << secondsUntilFirstExecution << std::endl;
        return secondsUntilFirstExecution;
    }

    // 当前时间已经超过了首次执行时间，计算下一个符合周期的执行时间
    std::chrono::system_clock::time_point nextExecutionTime = firstExecTimePoint;

    // std::cout << "Initial first execution time: " << TimePointToString(firstExecTimePoint) << std::endl;

    // 如果下一个执行时间小于当前时间，则计算接下来的周期
    while (nextExecutionTime <= now)
    {
        // std::cout << "Next execution time " << TimePointToString(nextExecutionTime) << " has passed. Adding another period." << std::endl;
        if (periodUnit == "day" || periodUnit == "天")
        {
            nextExecutionTime += std::chrono::hours(24 * periodValue);
        }
        else if (periodUnit == "hour" || periodUnit == "时")
        {
            nextExecutionTime += std::chrono::hours(periodValue);
        }
        else if (periodUnit == "minute" || periodUnit == "分")
        {
            nextExecutionTime += std::chrono::minutes(periodValue);
        }
        else
        {
            return -1;
        }
        // std::cout << "Updated next execution time: " << TimePointToString(nextExecutionTime) << std::endl;
    }

    // 计算下一个执行时间与当前时间的秒差
    auto secondsUntilNextExecution = std::chrono::duration_cast<std::chrono::milliseconds>(nextExecutionTime - now).count();
    // std::cout << firstExecTime << " Final execution time: " << TimePointToString(nextExecutionTime) << std::endl;
    return secondsUntilNextExecution;
}

std::string GetNowSubMilli(uint64_t milli)
{
    auto                      now = std::chrono::system_clock::now();
    std::chrono::milliseconds ms_to_subtract(milli);
    auto                      new_time = now - ms_to_subtract;

    // 将时间点转换为 time_t 类型
    std::time_t time = std::chrono::system_clock::to_time_t(new_time);

    std::tm* tm_info = std::localtime(&time);
    auto     ms      = std::chrono::duration_cast<std::chrono::milliseconds>(new_time.time_since_epoch()) % 1000;

    std::stringstream ss;
    ss << std::put_time(tm_info, "%Y-%m-%d %H:%M:%S") << "." << std::setfill('0') << std::setw(3) << ms.count();

    return ss.str();
}

bool SetSystemTimeFromMillis(int64_t time_millis)
{
    struct timeval tv;
    tv.tv_sec  = time_millis / 1000;           // 秒
    tv.tv_usec = (time_millis % 1000) * 1000;  // 微秒

    if (settimeofday(&tv, nullptr) == 0)
        return true;

    return false;
}

std::string GetTimeStringFromTimestamp(time_t time_stamp)
{
    // 将时间戳转换为 tm 结构（本地时间）
    struct tm* time_info = localtime(&time_stamp);

    // 格式化时间为 "年-月-日 时:分:秒" 的格式
    char time_string[20];  // "YYYY-MM-DD HH:MM:SS"
    strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", time_info);

    // 输出时间
    return std::string(time_string);
}

std::string GetOffsetTime(int hours_offset, int min_offset, const char* format)
{
    auto               now         = std::chrono::system_clock::now();
    auto               offset      = std::chrono::hours(hours_offset) + std::chrono::minutes(min_offset);
    auto               target_time = now - offset;
    std::time_t        t           = std::chrono::system_clock::to_time_t(target_time);
    std::tm            tm          = *std::localtime(&t);
    std::ostringstream oss;
    oss << std::put_time(&tm, format);
    return oss.str();
}

bool IsValidDateTime(const std::string& dateTime)
{
    std::regex pattern(R"(^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$)");
    return std::regex_match(dateTime, pattern);
}

long long ConvertToMilliseconds(const std::string& timeStr)
{
    std::tm tm = {};

    std::istringstream ss(timeStr);
    ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");

    if (ss.fail())
    {
        throw std::invalid_argument("Invalid time format");
    }

    std::time_t time = std::mktime(&tm);
    if (time == -1)
    {
        throw std::runtime_error("Error in converting time");
    }

    return static_cast<long long>(time) * 1000;
}

std::string TimestampToString(std::time_t timestamp)
{
    std::tm            tm_time = *std::localtime(&timestamp);
    std::ostringstream oss;
    oss << std::put_time(&tm_time, "%Y-%m-%d %H:%M:%S");
    return oss.str();
}

std::string GetCompileTime()
{
    std::filesystem::path exePath = std::filesystem::canonical("/proc/self/exe");
    auto                  ftime   = std::filesystem::last_write_time(exePath);

    // 转换为 system_clock 时间
    auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
        ftime - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now());

    std::time_t        cftime = std::chrono::system_clock::to_time_t(sctp);
    std::ostringstream oss;
    oss << std::put_time(std::localtime(&cftime), "%Y-%m-%d %H:%M:%S");
    return oss.str();
}
}  // namespace util::time
